/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.streamsets.pipeline.lib.xml;

import com.streamsets.pipeline.api.Field;
import com.streamsets.pipeline.api.impl.Utils;
import com.streamsets.pipeline.lib.io.ObjectLengthException;
import com.streamsets.pipeline.lib.io.OverrunException;
import com.streamsets.pipeline.lib.io.OverrunReader;

import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.io.Reader;

public class OverrunStreamingXmlParser  extends StreamingXmlParser {

  private final OverrunReader countingReader;
  private final int maxObjectLen;
  private long limit;
  private boolean overrun;
  private long initialPosition;

  public OverrunStreamingXmlParser(Reader reader, String recordElement, long initialPosition, int maxObjectLen)
      throws IOException, XMLStreamException {
    this(new OverrunReader(reader, OverrunReader.getDefaultReadLimit(), false, false), recordElement, initialPosition,
         maxObjectLen);
    this.initialPosition = initialPosition;
  }

  public OverrunStreamingXmlParser(OverrunReader reader, String recordElement, long initialPosition, int maxObjectLen)
      throws IOException, XMLStreamException {
    super(reader, recordElement, initialPosition);
    countingReader = (OverrunReader) getReader();
    countingReader.setEnabled(true);
    this.maxObjectLen = maxObjectLen;
    this.initialPosition = initialPosition;
  }

  @Override
  protected void fastForwardLeaseReader() {
    ((OverrunReader) getReader()).resetCount();
  }

  @Override
  protected boolean isOverMaxObjectLength() throws XMLStreamException {
    return (maxObjectLen > -1) && getReaderPosition() > limit;
  }

  @Override
  public Field read() throws IOException, XMLStreamException {
    Field field;
    Utils.checkState(!overrun, "The underlying input stream had an overrun, the parser is not usable anymore");
    countingReader.resetCount();
    limit = getReaderPosition() + maxObjectLen;
    try {
      field = super.read();
      if (isOverMaxObjectLength()) {
        throw new ObjectLengthException(Utils.format("XML Object at offset '{}' exceeds max length '{}'",
                                                        initialPosition, maxObjectLen), initialPosition);
      }
      initialPosition = getReaderPosition();
    } catch (XMLStreamException ex) {
      if (ex.getNestedException() != null && ex.getNestedException() instanceof OverrunException) {
        overrun = true;
        throw (OverrunException) ex.getNestedException();
      }
      throw ex;
    }
    return field;
  }

}
